using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using UnityEngine;
using UnityEngine.Networking;
using UObject = UnityEngine.Object;

public class HotUpdate : MonoBehaviour
{
    byte[] readOnlyPathFileListData;                //只读目录中filelist的数据
    byte[] serverFileListData;                      //服务器上filelist的数据

    internal class DownLoadFileInfo                 //下载文件信息
    {
        public string url;
        public string fileName;                     //bundle资源路径
        public DownloadHandler fileData;
    }

    private void Start()
    {
        if(IsFirstInstall())
        {
            ReleaseResources();
        }
        else
        {
            CheckUpdate();
        }
    }

    #region 文件处理模块

    /// <summary>
    /// 单个文件下载函数
    /// </summary>
    /// <param name="_fileInfo"></param>
    /// <param name="Complete"></param>
    /// <returns></returns>
    IEnumerator DownLoadFile(DownLoadFileInfo _fileInfo, Action<DownLoadFileInfo> Complete)
    {
        UnityWebRequest webRequest = UnityWebRequest.Get(_fileInfo.url);
        yield return webRequest.SendWebRequest();

        if(webRequest.result == UnityWebRequest.Result.ProtocolError || webRequest.result == UnityWebRequest.Result.ConnectionError)
        {
            Debug.LogError("文件下载出错:" + _fileInfo.url  );
            yield break;
        }

        _fileInfo.fileData = webRequest.downloadHandler;

        Complete?.Invoke(_fileInfo);

        webRequest.Dispose();
    }

    /// <summary>
    /// 多个文件下载函数
    /// </summary>
    /// <returns></returns>
    IEnumerator DownLoadFiles(List<DownLoadFileInfo> infos, Action<DownLoadFileInfo> Complete, Action DownLoadAllComplete)
    {
        foreach (var info in infos)
        {
            yield return DownLoadFile(info, Complete);
        }

        DownLoadAllComplete?.Invoke();
    }

    /// <summary>
    /// 获取文件信息
    /// </summary>
    /// <param name="fileData"></param>
    /// <returns></returns>
    private List<DownLoadFileInfo> GetFileList(string fileData, string path)
    {
        string content = fileData.Trim().Replace("\r", "");
        string[] files = content.Split('\n');

        List<DownLoadFileInfo> downLoadFileInfos = new List<DownLoadFileInfo>(files.Length);

        for(int i = 0; i < files.Length; i++) 
        {
            string[] info = files[i].Split('|');

            DownLoadFileInfo fileInfo = new DownLoadFileInfo();
            fileInfo.fileName = info[1];
            fileInfo.url = Path.Combine(path, info[1]);
            downLoadFileInfos.Add(fileInfo);
        }

        return downLoadFileInfos;
    }
    #endregion

    #region 热更新流程实现模块

    /// <summary>
    /// 检测是否初次安装
    /// </summary>
    /// <returns></returns>
    private bool IsFirstInstall()
    {
        //判断只读目录是否存在版本文件
        bool isExistsReadOnlyPath = FileUtil.IsFileExist(Path.Combine(PathUtil.ReadPath, AppConst.FileListName));

        //判断可读写目录是否存在版本文件
        bool isExistReadWritePath = FileUtil.IsFileExist(Path.Combine(PathUtil.ReadAndWritePath, AppConst.FileListName));

        return isExistsReadOnlyPath && !isExistReadWritePath;
    }

    //释放热更新资源
    private void ReleaseResources()
    {
        //获取版本文件路径
        string url = Path.Combine(PathUtil.ReadPath, AppConst.FileListName);

        DownLoadFileInfo fileInfo = new DownLoadFileInfo();

        fileInfo.url = url;

        //启动对应协程下载版本文件，在完成后回调相应函数
        StartCoroutine(DownLoadFile(fileInfo, OnDownLoadReadPathFileListComplete));
    }

    //在只读路径文件加载完成后调用
    private void OnDownLoadReadPathFileListComplete(DownLoadFileInfo info)
    {
        readOnlyPathFileListData = info.fileData.data;

        //解析版本文件，获取其内容
        List<DownLoadFileInfo> fileInfos = GetFileList(info.fileData.text, PathUtil.ReadPath);

        //启动相应协程释放版本文件中所有内容，完成后回调相应函数
        StartCoroutine(DownLoadFiles(fileInfos, OnRealeaseFileComplete, OnRealeaseAllFileComplete));
    }

    //所有文件释放完成后调用
    private void OnRealeaseAllFileComplete()
    {
        //在可读写目录中写入版本文件
        FileUtil.WriteFile(Path.Combine(PathUtil.ReadAndWritePath, AppConst.FileListName), readOnlyPathFileListData);

        //检测更新
        CheckUpdate();
    }

    //单个文件释放完成时调用
    private void OnRealeaseFileComplete(DownLoadFileInfo fileInfo)
    {
        Debug.Log("OnRealeaseFileCComplete:" +  fileInfo.url);

        //获取需要写入的文件
        string writeFile = Path.Combine(PathUtil.ReadAndWritePath, fileInfo.fileName);

        //写入文件
        FileUtil.WriteFile(writeFile, fileInfo.fileData.data);
    }

    private void CheckUpdate()
    {
        //获取服务器上版本文件的地址
        string url = Path.Combine(AppConst.ResourcesUrl, AppConst.FileListName);

        DownLoadFileInfo fileInfo = new DownLoadFileInfo();
        fileInfo.url = url;

        //启动下载协程
        StartCoroutine(DownLoadFile(fileInfo, OnDownLoadServerFileListComplete));
    }

    //在服务器上的版本文件下载完成时调用
    private void OnDownLoadServerFileListComplete(DownLoadFileInfo info)
    {
        serverFileListData = info.fileData.data;

        //获取服务器上版本文件的内容
        List<DownLoadFileInfo> fileInfos = GetFileList(info.fileData.text, AppConst.ResourcesUrl);

        //需要下载的文件集合
        List<DownLoadFileInfo> fileToDownLoadList = new List<DownLoadFileInfo>();

        //遍历服务器版本文件内容
        for (int i = 0; i < fileInfos.Count; i++) 
        {
            //判断本地的可读写目录是否存在对应的服务器版本文件内容
            string localFile = Path.Combine(PathUtil.ReadAndWritePath, fileInfos[i].fileName);
            if(!FileUtil.IsFileExist(localFile))
            {
                //不存在则将其加入需要下载的文件集合中
                fileInfos[i].url = Path.Combine(AppConst.ResourcesUrl, fileInfos[i].fileName);
                fileToDownLoadList.Add(fileInfos[i]);
            }
        }

        if(fileToDownLoadList.Count > 0) 
        {
            StartCoroutine(DownLoadFiles(fileInfos, OnUpdateFileComplete, OnUpdateAllFileComplete));
        }
        else
        {
            //进入游戏
            EnterGame();
        }
    }


    private void OnUpdateAllFileComplete()
    {
        FileUtil.WriteFile(Path.Combine(PathUtil.ReadAndWritePath, AppConst.FileListName), serverFileListData);

        EnterGame();
    }

    private void OnUpdateFileComplete(DownLoadFileInfo fileInfo)
    {
        Debug.Log("OnUpdateFileComplete:" + fileInfo.url);

        //获取需要写入的文件
        string writeFile = Path.Combine(PathUtil.ReadAndWritePath, fileInfo.fileName);

        //写入文件
        FileUtil.WriteFile(writeFile, fileInfo.fileData.data);
    }


    #endregion

    private void EnterGame()
    {
        Manager.Resources.ParseVersionFile();
        Manager.Resources.LoadUI("TestUI", OnCompleted);
    }

    private void OnCompleted(UObject obj)
    {
        GameObject go = Instantiate(obj) as GameObject;
        go.transform.SetParent(this.transform);
        go.SetActive(true);
        go.transform.localPosition = Vector3.zero;
        go.transform.localScale = Vector3.one;
    }
}
